using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using T.FAS.Runtime.Metadata.Entity;
using T.FAS.Runtime.Action.SPI;
using T.FAS.Runtime.Base.Entity;
using T.FAS.Runtime.DataAccess.Service;
using T.FAS.Runtime.Metadata.Entity.DataModel;
using T.FAS.Runtime.Expression.Entity;
using T.FAS.Runtime.Expression.Service;

namespace T.FAS.Runtime.Action.Engine
{
    public class ServerJSActionEngine : IActionExecuteProxy, IActionDataResultBuilder, IActionExecuteHandler
    {
        public void AfterExecute(SPI.ActionExecContext execContext)
        {
           
        }

        public void BeforeExecute(SPI.ActionExecContext execContext)
        {
           
        }

        public TResult BuildActionDataResult(SPI.ActionExecContext context, object dataResult)
        {
            var actionMetaData = ActionRTPUtil.ActionMetadataValid(context.MetaData);
            if (actionMetaData.ReturnValueType == Metadata.Entity.ReturnValueType.Void)
                return null;
            if (actionMetaData.Method == null)
                throw new FasException(ActionExecuteExceptionCode.ActionExecuteEngineExecuteError, string.Format(CommonStringClassEngine.ActionExec_MissingMethodDefinition,actionMetaData.Code));
            var outputResult = actionMetaData.MethodParams.Find(x => x.ParamType == ParamType.Out);
            if (outputResult == null)
                throw new FasException(ActionExecuteExceptionCode.ActionExecuteEngineExecuteError,string.Format(CommonStringClassEngine.ActionExec_MissingMethodRetuanValueTypeDefinition,actionMetaData.Code));

            var expResult = (dataResult as ExprResult).Result;
            if (outputResult.ParamSourceType == ParamSourceType.Basic)
            {
                var tResult = new TCommonResult<TRecord>()
                {
                    ActionCode = actionMetaData.Code,
                    ActionName = actionMetaData.Name,
                    Count = 0,
                    ResultType = actionMetaData.ReturnValueType == ReturnValueType.Record ? DataType.Record : DataType.List,
                    DataContent = expResult,
                };
                tResult.Schema = BasicTypeData.BuildSchema(outputResult.ParamValueType);
                var isList = actionMetaData.ReturnValueType == Metadata.Entity.ReturnValueType.List ? true : false;
                var isUserDefineResult = UserDefineResultHandler.ConvertToTResultByMappingJS(dataResult, context, tResult);
                if (!isUserDefineResult)
                {
                    if (tResult.ResultType == DataType.Record)
                    {
                        tResult.Record = new TRecord();
                        tResult.Record.SetValue(BasicTypeData.DefaultPropertyName, expResult);
                    }
                    else
                    {
                        var dataList = expResult as object[];
                        foreach (var item in dataList)
                        {
                            if (tResult.List == null)
                            {
                                tResult.Schema = BasicTypeData.BuildSchema(outputResult.ParamValueType);
                                tResult.List = new TArray<TRecord>(new TEntity());
                            }
                            tResult.List.Add(new TRecord(new Dictionary<string, object>() { { BasicTypeData.DefaultPropertyName, item } }));
                        }
                        tResult.Record = new TRecord();
                    }
                }
                return tResult;
            }
            else if (outputResult.ParamSourceType == ParamSourceType.CDO ||
                outputResult.ParamSourceType == ParamSourceType.DO ||
                outputResult.ParamSourceType == ParamSourceType.DQ ||
                outputResult.ParamSourceType == ParamSourceType.EO ||
                outputResult.ParamSourceType == ParamSourceType.SDO)
            {
                var tResult = new TCommonResult<TRecord>()
                {
                    ActionCode = actionMetaData.Code,
                    ActionName = actionMetaData.Name,
                    Count = 0,
                    ResultType = actionMetaData.ReturnValueType == ReturnValueType.Record ? DataType.Record : DataType.List,
                    DataContent = dataResult,
                };
                tResult.Schema = new TSchema()
                {
                    Entity = new TEntity()
                };

                var isUserDefineResult = UserDefineResultHandler.ConvertToTResultByMappingJS(dataResult, context, tResult);
                if (!isUserDefineResult)
                {

                    if (actionMetaData.ReturnValueType == ReturnValueType.Record)
                    {
                        tResult.List = new TArray<TRecord>(tResult.Schema.Entity);
                        var tmp = JsonConvert.DeserializeObject<JObject>(JsonConvert.SerializeObject(expResult));
                        tResult.Record = TRecord.BuildRecord(tmp);
                    }
                    else
                    {
                        var list = JsonConvert.DeserializeObject<List<JObject>>(JsonConvert.SerializeObject(expResult));
                        if (list != null)
                        {
                            foreach (var item in list)
                            {
                                tResult.List.Add(TRecord.BuildRecord(item as JObject));
                            }
                        }
                        tResult.Record = new TRecord();
                    }
                }


                return tResult;
                #region 旧版本 存在问题返回值都是JToken，在Jint中进行赋值时异常
                    //fix:注释代码删除 2024.3.29
                    #endregion
            }

            return null;
        }

        public object Execute(SPI.ActionExecContext context)
        {
            ExprResult result = null;
            try
            {
                var action = ActionRTPUtil.ActionMetadataValid(context.MetaData);
                if (action.ActionType != Metadata.Entity.ActionType.JSScript)
                    throw new FasException(ActionExecuteExceptionCode.ActionExecuteEngineExecuteError, string.Format(CommonStringClassEngine.ServerJSActionEngine_Execute,action.Code));
                if (action.Method == null)
                    throw new FasException(ActionExecuteExceptionCode.ActionExecuteEngineExecuteError, string.Format(CommonStringClassEngine.ServerJSActionEngine_Execute1,action.Code));
                if (string.IsNullOrEmpty(action.Method.MethodContent))
                    throw new FasException(ActionExecuteExceptionCode.ActionExecuteEngineExecuteError, string.Format(CommonStringClassEngine.ServerJSActionEngine_Execute2, action.Code));


                var jsContent = action.Method.MethodContent;
                var paramters = context.DataContent;
                if (action.MethodParams != null && action.MethodParams.Count > 0)
                {
                    var inParamsAndVariable = action.MethodParams.FindAll(x => x.ParamType == Metadata.Entity.ParamType.In || x.ParamType == ParamType.Variable);
                    if (inParamsAndVariable != null && inParamsAndVariable.Count > 0 && !string.IsNullOrEmpty(action.ParamMappingJS))
                    {
                        paramters = ActionParamMappingHandler.BuildParamsByMapping(context);
                        if (context != null && context.DataContent != null)
                        {
                            foreach (var item in context.DataContent)
                            {
                                if (!paramters.ContainsKey(item.Key))
                                    paramters[item.Key] = context.DataContent[item.Key];
                            }
                        }
                        //将更新了变量值的Action刷新到上下文中的Action元数据中
                        context.MetaData = action;

                    }
                }

                result = ExprService.GetInstance.JSExecute(jsContent, paramters);
                if (result.State == ResultState.Fail)
                {
                    throw new FasException(ActionExecuteExceptionCode.ActionExecuteEngineExecuteError, string.Format(CommonStringClassEngine.ExpressionScriptExecFail, result?.RowNum,result?.ErrMsg));
                }
                return result;
            }
            catch (Exception e)
            {
                throw e;
            }
        }

        public void ExecuteError(SPI.ActionExecContext execContext, Exception exception)
        {
            
        }

        public object ExecuteWithSubEngine(IActionExecuteEngine subEngine, ActionExecContext context)
        {
            throw new NotImplementedException();
        }
    }
}
